package com.agileai.bpm.webservice;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import com.agileai.bpm.common.BPMConstants;
import com.agileai.bpm.domain.define.Activity;
import com.agileai.bpm.domain.define.Process;
import com.agileai.bpm.domain.running.ActivityInstance;
import com.agileai.bpm.domain.running.DelegateItem;
import com.agileai.bpm.domain.running.HandleItem;
import com.agileai.bpm.domain.running.InvokeItem;
import com.agileai.bpm.domain.running.LaunchItem;
import com.agileai.bpm.domain.running.ProcessInstance;
import com.agileai.bpm.domain.running.SubmitItem;
import com.agileai.bpm.domain.running.WorkAttributes;
import com.agileai.bpm.domain.running.WorkItem;
import com.agileai.bpm.service.running.ProcessHandle;
import com.agileai.bpm.service.running.ProcessHandle.UserSeqType;
import com.agileai.bpm.service.running.RunningException;
import com.agileai.hotweb.common.BeanFactory;
import com.agileai.hotweb.common.ResultStatus;

public class BPMServiceImpl implements BPMService {
	private ProcessHandle processHandle = null;
	
	public String[] getProcessId8InstId(String processCode,String bizRecordId) {
		return getProcessHandle().getProcessId8InstId(processCode, bizRecordId);
	}
	
	public List<WorkItem> findWorkItems(String userCode) {
		List<WorkItem> result = null;
		try {
			result = this.getProcessHandle().findWorkItemList(userCode,UserSeqType.USER_CODE);
		} catch (RunningException e) {
			e.printStackTrace();
		}
		return result;
	}	

	public boolean isOwnTask(String userCode, String processInstanceId, String activityCode) {
		boolean result = false;
		try {
			String userId = getProcessHandle().findUserId(userCode);
			result = this.isOwnTask(userId, processInstanceId, activityCode);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public String getCurrentProcessId(String processCode) {
		String result = null;
		try {
			result = this.getProcessHandle().findCurrentProcessId(processCode);
		} catch (RunningException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public ResultStatus launchProcess(LaunchWorkItem launchWorkItem,boolean skipFirstNode) {
		ResultStatus result = new ResultStatus();
		try {
			String processId = launchWorkItem.getProcessId();
			LaunchItem workItem = new LaunchItem(processId);
			this.buildLaunchWorkItem(workItem, launchWorkItem);
			this.getProcessHandle().launchProcess(workItem,skipFirstNode);
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}
	
	private void buildLaunchWorkItem(LaunchItem workItem,LaunchWorkItem launchWorkItem) throws RunningException{
		WorkAttributes workAttributes = workItem.getWorkAttributes();
		workItem.setTitle(launchWorkItem.getTitle());
		String userId = getProcessHandle().findUserId(launchWorkItem.getUserCode());
		workItem.setUserId(userId);
		workItem.setBizRecordId(launchWorkItem.getBizRecordId());
		workItem.setBizRecordCode(launchWorkItem.getBizRecordCode());
		List<BizAttribute> bpmAttributeList = launchWorkItem.getAttributeList();
		if (bpmAttributeList != null){
			for (int i=0;i < bpmAttributeList.size();i++){
				BizAttribute bpmAttribute = bpmAttributeList.get(i);
				String key = bpmAttribute.getCode();
				String value = bpmAttribute.getValue();
				workAttributes.addAttribute(key, value);
			}
		}
	}
	
	public ResultStatus submitProcess(SubmitWorkItem submitWorkItem) {
		ResultStatus result = new ResultStatus();
		String processId = submitWorkItem.getProcessId();
		String processInstanceId = submitWorkItem.getProcessInstanceId();
		try {
			ProcessInstance processInstance = this.getProcessHandle().retrieveProcessInstance(processInstanceId);
			if (!ProcessInstance.Status.running.equals(processInstance.getStatus())){
				throw new RunningException("process is not in running status !");
			}
			String activityCode = submitWorkItem.getActivityCode();
			SubmitItem workItem = new SubmitItem(processId,processInstanceId,activityCode);
			
			this.buildSubmitWorkItem(workItem, submitWorkItem);
			this.getProcessHandle().submitProcess(workItem,processInstance);
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}

	@Override
	public ResultStatus invokeProcess(InvokeWorkItem invokeWorkItem) {
		ResultStatus result = new ResultStatus();
		try {
			String activityCode = invokeWorkItem.getActivityCode();
			String processCode = invokeWorkItem.getProcessCode();
			String bizRecordId = invokeWorkItem.getBizRecordId();
			String[] processId8InstId = this.getProcessId8InstId(processCode, bizRecordId);
			if (processId8InstId != null){
				String processId = processId8InstId[0];
				String processInstanceId = processId8InstId[1];
				InvokeItem invokeItem = new InvokeItem(processId);
				
				ProcessInstance processInstance = this.getProcessHandle().retrieveProcessInstance(processInstanceId);
				if (!ProcessInstance.Status.running.equals(processInstance.getStatus())){
					throw new RunningException("process is not in running status !");
				}
				
				if (!processInstance.getActivityInstances().containsKey(activityCode)){
					throw new RunningException("current process is not in a watch node !");
				}
				
				this.buildInvokeWorkItem(invokeItem, invokeWorkItem);
				this.getProcessHandle().invokeProcess(invokeItem, processInstance);
				result.setSuccess(true);
			}else{
				throw new RunningException("process instance is not exists!");
			}
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}	
	

	@Override
	public ResultStatus delegateProcess(DelegateWorkItem delegateWorkItem) {
		ResultStatus result = new ResultStatus();
		String processInstanceId = delegateWorkItem.getProcessInstanceId();
		try {
			ProcessInstance processInstance = this.getProcessHandle().retrieveProcessInstance(processInstanceId);
			if (!ProcessInstance.Status.running.equals(processInstance.getStatus())){
				throw new RunningException("process is not in running status !");
			}
			
			String userCode = delegateWorkItem.getUserCode(); 
			String activityCode = delegateWorkItem.getActivityCode();
			String userId = this.getProcessHandle().findUserId(userCode);;
			String delegateeId = this.getProcessHandle().findUserId(delegateWorkItem.getDelegatee());
			String activityInstId = this.getProcessHandle().findActivityInstId(processInstanceId, userCode, activityCode);
			
			DelegateItem workItem = new DelegateItem();
			workItem.setProcessInstId(processInstanceId);
			workItem.setActivityInstId(activityInstId);
			workItem.setUserId(userId);
			workItem.setDelegateeId(delegateeId);
			
			this.getProcessHandle().delegateProcess(workItem);
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}	
	
	private void buildSubmitWorkItem(SubmitItem workItem,SubmitWorkItem processWorkItem) throws RunningException{
		WorkAttributes workAttributes = workItem.getWorkAttributes();
		workItem.setTitle(processWorkItem.getTitle());
		String userId = getProcessHandle().findUserId(processWorkItem.getUserCode());
		workItem.setUserId(userId);
		List<BizAttribute> bpmAttributeList = processWorkItem.getAttributeList();
		if (bpmAttributeList != null){
			for (int i=0;i < bpmAttributeList.size();i++){
				BizAttribute bpmAttribute = bpmAttributeList.get(i);
				String key = bpmAttribute.getCode();
				String value = bpmAttribute.getValue();
				workAttributes.addAttribute(key, value);
			}
		}
	}	
	
	private void buildInvokeWorkItem(InvokeItem workItem,InvokeWorkItem invokeWorkItem) throws RunningException{
		WorkAttributes workAttributes = workItem.getWorkAttributes();
		workItem.setTitle(BPMConstants.DEF_INVOKE_OPERA_DESC);
		workItem.setActivityCode(invokeWorkItem.getActivityCode());
		workItem.setBizRecordId(invokeWorkItem.getBizRecordId());
		List<BizAttribute> bpmAttributeList = invokeWorkItem.getAttributeList();
		if (bpmAttributeList != null){
			for (int i=0;i < bpmAttributeList.size();i++){
				BizAttribute bpmAttribute = bpmAttributeList.get(i);
				String key = bpmAttribute.getCode();
				String value = bpmAttribute.getValue();
				workAttributes.addAttribute(key, value);
			}
		}
	}		
	
	public ResultStatus resumeProcess(HandleWorkItem processWorkItem) {
		ResultStatus result = new ResultStatus();
		String processId = processWorkItem.getProcessId();
		String processInstanceId = processWorkItem.getProcessInstanceId();
		String userCode = processWorkItem.getUserCode();
		try {
			String userId = getProcessHandle().findUserId(userCode);
			HandleItem workItem = new HandleItem(userId,processId,processInstanceId);
			this.getProcessHandle().resumeProcess(workItem);
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}
	
	public ResultStatus suspendProcess(HandleWorkItem processWorkItem) {
		ResultStatus result = new ResultStatus();
		String processId = processWorkItem.getProcessId();
		String processInstanceId = processWorkItem.getProcessInstanceId();
		String userCode = processWorkItem.getUserCode();
		try {
			String userId = getProcessHandle().findUserId(userCode);
			HandleItem workItem = new HandleItem(userId,processId,processInstanceId);
			this.getProcessHandle().suspendProcess(workItem);
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}

	public ResultStatus terminateProcess(HandleWorkItem processWorkItem) {
		ResultStatus result = new ResultStatus();
		String processId = processWorkItem.getProcessId();
		String processInstanceId = processWorkItem.getProcessInstanceId();
		String userCode = processWorkItem.getUserCode();
		try {
			String userId = getProcessHandle().findUserId(userCode);
			HandleItem workItem = new HandleItem(userId,processId,processInstanceId);
			this.getProcessHandle().terminateProcess(workItem);
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}
	
	public ProcessDefine retrieveProcessDefine(String processId) {
		ProcessDefine result = new ProcessDefine();
		try {
			Process process = this.getProcessHandle().retrieveProcess(processId);
			if (process != null){
				result.setId(process.getId());
				result.setCode(process.getCode());
				result.setName(process.getName());
				result.setDesc(process.getDesc());
				result.setDefineXML(process.getDefineXML());
				result.setGraphJSON(process.getGraphJSON());
				result.setStatus(process.getStatus());
				result.setVersion(process.getVersion());
			}
		} catch (RunningException e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public ProcessProfile retrieveProcessProfile(String processInsId) {
		ProcessProfile result = new ProcessProfile();
		try {
			ProcessInstance processInstance = this.getProcessHandle().retrieveProcessInstance(processInsId);
			if (processInstance != null){
				Process process = processInstance.getProcess();
				result.setId(processInstance.getId());
				result.setCode(process.getCode());
				result.setName(process.getName());
				result.setTitle(processInstance.getTitle());
				result.setDefineXML(process.getDefineXML());
				result.setGraphJSON(process.getGraphJSON());
				result.setStatus(processInstance.getStatus());
				result.setBizRecordId(processInstance.getBizRecordId());
				result.setLaucherId(processInstance.getLaucherId());
				result.setLaucherName(processInstance.getLaucherName());
				result.setLaucheTime(processInstance.getLaucheTime());
				result.setOperaterId(processInstance.getOperaterId());
				result.setOperaterName(processInstance.getOperaterName());
				result.setOperateTime(processInstance.getOperateTime());
				
				HashMap<String, ActivityInstance> maps = processInstance.getActivityInstances();
				if (maps != null){
					Iterator<String> keys = maps.keySet().iterator();
					while (keys.hasNext()){
						String key = keys.next();
						ActivityInstance activityInstance = maps.get(key);
						Activity activity = activityInstance.getActivity();
						String code = activity.getCode();
						result.getActiveNodes().add(code);
					}
				}
			}
		} catch (RunningException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	private ProcessHandle getProcessHandle() {
		if (this.processHandle == null){
			BeanFactory beanFactory = BeanFactory.instance();
			Object handlerObject = beanFactory.getBean("processHandle");
			this.processHandle = (ProcessHandle)handlerObject;	
		}
		return processHandle;
	}

	@Override
	public ResultStatus saveDraft(DraftWorkItem draftWorkItem) {
		ResultStatus result = new ResultStatus();
		try {
			String userId = getProcessHandle().findUserId(draftWorkItem.getUserCode());
			String processId = getProcessHandle().findCurrentProcessId(draftWorkItem.getProcessCode());
			getProcessHandle().saveDraft(processId, userId, draftWorkItem.getBizRecordId(),draftWorkItem.getBizTitle(),draftWorkItem.getBizRecordCode());
			result.setSuccess(true);
		} catch (RunningException e) {
			e.printStackTrace();
			result.setErrorMsg(e.getLocalizedMessage());
		}
		return result;
	}
}